package com.mingjieli.excel.form;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.mingjieli.excel.ExcelRuntimeException;
import com.mingjieli.excel.ExcelUtil;
import com.mingjieli.excel.ExcelExporter;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 用于带有业务子表的Excel导出
 * @author mingjie
 * @since 2020-05-19
 */
public class FormTemplateExcelExporter implements ExcelExporter {
	private static final Logger           logger         = LoggerFactory.getLogger(FormTemplateExcelExporter.class);
	private              final Workbook         workbook;
	private              final Sheet     sheet;
	//保存整个sheet的业务子表模型
	private              List<GridModel> gridModelList = null;
	//保存整个sheet的业务子表对应的数据
	private              List<?>[]       gridDataArray = null;

	/***
	 * 根据模板在内存中创建Excel
	 * @param templateStream 模板Excel对应的inputStream
	 */
	public FormTemplateExcelExporter(InputStream templateStream) {
		Workbook workbook;
		try {
			workbook = new XSSFWorkbook(templateStream);
			this.workbook = workbook;
			this.sheet = workbook.getSheetAt(0);
		} catch (IOException e) {
			throw new ExcelRuntimeException("无法读取模板文件", e);
		}

	}

	/**
	 * 如果模板存在需要替换值的表单项，这由此带入；为了简化代码及操作，存在表单项的话，需要用#开头的字符集实现占位;
	 * 此方法可重复调用，但是一旦被替换，则不可恢复
	 * @param formItemValues 需要替换的数据集合，非业务子表
	 * @return 类本身
	 */
	public FormTemplateExcelExporter withFormItemValues(Map<String, String> formItemValues){
		if (formItemValues == null || formItemValues.isEmpty()) {
			return this;
		}

		try {
			for (Row row : sheet) {
				for (Cell c : row) {
					if (c.getStringCellValue().startsWith("#")) {
						String filedName = c.getStringCellValue().substring(1);
						setValue(c, formItemValues.get(filedName));
					}
				}
			}
		} catch (Exception exception) {
			logger.warn(exception.getMessage(), exception);
		}
		return this;
	}

	/**
	 * 如果存在声明文件，带上声明inputStream
	 * @param statementStream 子表声明文件对应的InputStream
	 * @return 类本身
	 */
	public FormTemplateExcelExporter withStatement(InputStream statementStream){
		gridModelList = new GridModelParser().parse(statementStream);
		return this;
	}

	/**
	 * @param gridDataArray 每个业务子表格对应的数据，每一个业务子表的数据为List，List的item可以是数组，Map或者java entity
	 * @return 类本身
	 */
	public FormTemplateExcelExporter withGridData(List<?>... gridDataArray) {
		this.gridDataArray = gridDataArray;
		return this;
	}

	private void fillData() {
		for (int i = gridModelList.size() - 1; i >= 0; i--) {
			GridModel gridModel = gridModelList.get(i);
			List<?> gridData = gridDataArray[i];
			fillGridData(gridModel, gridData);
		}
	}

	private void fillGridData(GridModel gridModel, List<?> gridData) {
		if (gridModel.getDirect() == GridModel.DIRECT.HORIZONTAL) {
			fillHorizontalGridData(gridModel, gridData);
		} else {
			fillVerticalGridData(gridModel, gridData);
		}
	}

	private void fillVerticalGridData(GridModel gridModel, List<?> gridData) {
		possibleInsertCell(gridData.size(), gridModel);
		for (int i = 0; i < gridData.size(); i++) {
			Object items = gridData.get(i);
			if (items.getClass().isArray() || items instanceof Map) {
				items = JSON.toJSON(items);
			}

			int size = gridModel.getItemTitleList().size();
			for (int j = 0; j < size; j++) {
				TitleModel itemTitleModel = gridModel.getItemTitleList().get(j);
				int rowNum = itemTitleModel.getStartRowNum();
				int column = itemTitleModel.getStartColumnNum() + i + 1;
				Row currentRow = this.sheet.getRow(rowNum);
				Cell currentCell = currentRow.getCell(column, Row.CREATE_NULL_AS_BLANK);
				Object value = getValue(items, j, itemTitleModel.getName());
				setValue(currentCell, value);
			}

		}
	}

	/**
	 * 如果数据超过模板的宽度，需要插入cell
	 * @param dataSize 数据大小
	 * @param gridModel 业务子表模型
	 */
	private void possibleInsertCell(int dataSize, GridModel gridModel) {
		int bodyWidth = gridModel.getBodyWidth();
		if (dataSize <= bodyWidth) {
			return;
		}
		int startColumnNum = gridModel.getBodyStartColumnNum() + bodyWidth;
		int num = dataSize - bodyWidth;
		for (int i = 0; i < num; i++) {
			for (TitleModel titleModel : gridModel.getItemTitleList()) {
				Cell preHorizontalCell = sheet.getRow(titleModel.getStartRowNum()).getCell(startColumnNum + i - 1);
				Cell currentCell = sheet.getRow(titleModel.getStartRowNum())
						.getCell(startColumnNum + i, Row.CREATE_NULL_AS_BLANK);
				currentCell.setCellStyle(preHorizontalCell.getCellStyle());
			}
		}

	}

	private void fillHorizontalGridData(GridModel gridModel, List<?> gridData) {
		possibleInsertRow(gridData.size(), gridModel);

		for (int i = 0; i < gridData.size(); i++) {
			Object items = gridData.get(i);
			if (items.getClass().isArray() || items instanceof Map) {
				items = JSON.toJSON(items);
			}

			int size = gridModel.getItemTitleList().size();
			for (int j = 0; j < size; j++) {
				TitleModel titleModel = gridModel.getItemTitleList().get(j);
				Cell cell = sheet.getRow(titleModel.getStartRowNum() + titleModel.getHeight() + i)
						.getCell(titleModel.getStartColumnNum(), Row.CREATE_NULL_AS_BLANK);
				Object value = getValue(items, j, titleModel.getName());
				setValue(cell, value);
			}
		}

	}

	private Object getValue(Object items, int index, String name) {
		if (items instanceof JSONArray) {
			return ((JSONArray) items).get(index);
		} else if (items instanceof JSONObject && StringUtils.isNotBlank(name)) {
			return ((JSONObject) items).get(name);
		} else if (StringUtils.isNotBlank(name)) {
			//根据反射调用get方法获取对应的值
			String[] fieldNames = name.split("\\.");
			Object value = items;
			for(String fieldName : fieldNames){
				if(value == null){
					break;
				}
				value = reflectValue(value, fieldName);
			}
			return value;
		}
		return null;
	}

	/**
	 * 根据反射取值
	 * @param obj 反射对象
	 * @param fieldName 字段名称，会解析成get方法
	 * @return 对应的值
	 */
	private Object reflectValue(Object obj, String fieldName) {
		try {
			String methodName = "get" + Character.toUpperCase(fieldName.charAt(0));
			if (fieldName.length() > 1) {
				methodName = methodName + fieldName.substring(1);
			}
			Method method = obj.getClass().getMethod(methodName);
			return method.invoke(obj);
		} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
			logger.warn(e.getMessage(), e);
		}
		return null;
	}

	/**
	 * 如果数据行数，大于子表的body的行数，则需要插入新的行
	 * @param dataSize 数据大小
	 * @param gridModel 业务子表模型
	 */
	private void possibleInsertRow(int dataSize, GridModel gridModel) {
		int bodyHeight = gridModel.getBodyHeight();
		if (dataSize <= bodyHeight) {
			return;
		}
		//数据行数大于body高度，后面的表格下移

		int startRowNum = gridModel.getBodyStartRowNum() + bodyHeight;
		int num = dataSize - bodyHeight; //需要插入的行数
		sheet.shiftRows(startRowNum, sheet.getLastRowNum(), num, true, true);

		for (int i = 0; i < num; i++) {
			ExcelUtil.copyRows(startRowNum - 1, startRowNum, startRowNum + i, sheet);
		}

		CellRangeAddress region;
		for (int i = 0; i < sheet.getNumMergedRegions(); i++) {
			region = sheet.getMergedRegion(i).copy();

			if (region.getFirstRow() == gridModel.getColumnTitle().getStartRowNum()
					&& region.getFirstColumn() == gridModel.getColumnTitle().getStartColumnNum()) {
				sheet.removeMergedRegion(i);
				region.setLastRow(region.getLastRow() + num);
				sheet.addMergedRegion(region);
				break;
			}
		}

	}

	public Workbook workbook() {
		if (gridDataArray.length != gridModelList.size()) {
			throw new ExcelRuntimeException("业务子表模型数量和业务数据数量不匹配");
		}

		fillData();
		return this.workbook;
	}

	/**
	 * 设置单元格的值
	 * @param cell 单元格
	 * @param value 值
	 */
	private void setValue(Cell cell, Object value) {
		if (value == null) {
			return;
		}

		int cellType = cell.getCellType();
		if (cellType == Cell.CELL_TYPE_NUMERIC) {
			if (HSSFDateUtil.isCellDateFormatted(cell)) {
				cell.setCellValue((Date) value);
			} else {
				cell.setCellValue(value.toString());
			}
		} else if (cellType == Cell.CELL_TYPE_STRING) {
			cell.setCellValue(value.toString());
		} else if (cellType == Cell.CELL_TYPE_BOOLEAN) {
			cell.setCellValue((Boolean) value);
		} else {
			cell.setCellType(Cell.CELL_TYPE_STRING);
			cell.setCellValue(value.toString());
		}
	}

}
